/*  _______________________________________________________________________

    DAKOTA: Design Analysis Kit for Optimization and Terascale Applications
    Copyright (c) 2010, Sandia National Laboratories.
    This software is distributed under the GNU Lesser General Public License.
    For more information, see the README file in the top Dakota directory.
    _______________________________________________________________________ */

//- Class:       rSQPOptimizer
//- Description: Implementation code for the rSQPOptimizer class
//- Owner:       Roscoe Bartlett
//- Checked by:

#include "system_defs.h"
#include "rSQPOptimizer.H"
#include "ProblemDescDB.H"

#include "MoochoPack/configurations/MoochoSolver.hpp"
#include "DenseLinAlgPack/src/LinAlgOpPack.hpp"
#include "ThrowException.hpp"

static const char rcsId[]="@(#) $Id: rSQPOptimizer.C 7029 2010-10-22 00:17:02Z mseldre $";

namespace {

DenseLinAlgPack::DVectorSlice my_vec_view( Dakota::RealVector& v )
{
	return DenseLinAlgPack::DVectorSlice( &const_cast<Dakota::RealVector&>(v)[0], v.length() );
}

const DenseLinAlgPack::DVectorSlice my_vec_view( const Dakota::RealVector& v )
{
	return DenseLinAlgPack::DVectorSlice( &const_cast<Dakota::RealVector&>(v)[0], v.length() );
}

} // end namespace

//
// NLPDakota
//

namespace NLPInterfacePack {

// Constructors / initializers

NLPDakota::NLPDakota(
	Dakota::Model               *model
	,rSQPOptimizer            *dakota_rsqp_opt
	,size_type                num_lin_eq
	,size_type                num_nonlin_eq
	,size_type                num_lin_ineq
	,size_type                num_nonlin_ineq
	)
	:model_(model)
	,dakota_rsqp_opt_(dakota_rsqp_opt)
	,multi_obj_weights_(dakota_rsqp_opt->primary_response_fn_weights())
	,num_lin_eq_(num_lin_eq)
	,num_nonlin_eq_(num_nonlin_eq)
	,num_lin_ineq_(num_lin_ineq)
	,num_nonlin_ineq_(num_nonlin_ineq)
	,is_initialized_(false)
	,n_orig_(0)
	,f_orig_updated_(false)
	,c_orig_updated_(false)
	,h_orig_updated_(false)
	,Gf_orig_updated_(false)
	,Gc_orig_updated_(false)
	,Gh_orig_updated_(false)
{
	THROW_EXCEPTION(
		model_ == NULL, std::logic_error
		,"NLPDakota::NLPDakota(model): Error, model can not be NULL!"
		);
	// Get a representation for an invalid value
	typedef std::numeric_limits<value_type> nl_t;
	invalid_func_value_ = nl_t::has_infinity
		? nl_t::infinity()
		: ( nl_t::has_quiet_NaN
			? nl_t::quiet_NaN()
			: nl_t::max()        // If Inf and Nan are not supported just put in a large value
			);
	// Get the dummy value for failure
	const Dakota::RealVector &failure_values = model_->problem_description_db().get_rdv("interface.failure_capture.recovery_fn_vals");
	if(failure_values.length()) {
		// Dakota will return  failure_values in case the simulation failes
		dakota_failed_value_ = failure_values[0]; // Just watch out for the first one
	}
	else { // Dakota will not return a failed value
		dakota_failed_value_ = invalid_func_value_; // This should not be generated by accident
	}
}

// Overridden public members from NLP

void NLPDakota::initialize(bool test_setup)
{

	if(is_initialized_) {
		NLPSerialPreprocessExplJac::initialize(test_setup);
		return;
	}

	//
	// Get the dimensions of the problem
	//
	
	const int num_responces = model_->num_functions();
	num_objectives_         = dakota_rsqp_opt_->num_objectives();
	
	n_orig_  = model_->cv();
	m_orig_  = num_lin_eq_   + num_nonlin_eq_;
	mI_orig_ = num_lin_ineq_ + num_nonlin_ineq_;

	//
	// Get the data for the problem
	//
	
	// xinit, xl, xu
	xinit_orig_  = my_vec_view(model_->continuous_variables());
	xl_orig_     = my_vec_view(model_->continuous_lower_bounds());
	xu_orig_     = my_vec_view(model_->continuous_upper_bounds());
	
	has_var_bounds_ = true; // We will assume this here

	// hl, hu
	hl_orig_.resize(mI_orig_);
	hu_orig_.resize(mI_orig_);
	if(num_lin_ineq_) {
		hl_orig_(1,num_lin_ineq_)          = my_vec_view(dakota_rsqp_opt_->lin_ineq_lb());
		hu_orig_(1,num_lin_ineq_)          = my_vec_view(dakota_rsqp_opt_->lin_ineq_ub());
	}
	if(num_nonlin_ineq_) {
		hl_orig_(num_lin_ineq_+1,mI_orig_) = my_vec_view(dakota_rsqp_opt_->nonlin_ineq_lb());
		hu_orig_(num_lin_ineq_+1,mI_orig_) = my_vec_view(dakota_rsqp_opt_->nonlin_ineq_ub());
	}

	// Gc, Gh
	Gc_orig_nz_ = n_orig_ * m_orig_;
	Gh_orig_nz_ = n_orig_ * mI_orig_;

	// Dakota interface stuff
	dakota_x_.reshape(n_orig_);
	dakota_asv_.reshape(num_responces);
	dakota_functions_.reshape(num_responces);

	is_initialized_ = true;
	NLPSerialPreprocessExplJac::initialize(test_setup);

//	throw std::logic_error("NLPDakota::initialize(): End, dam it!");

}

bool NLPDakota::is_initialized() const
{
	return is_initialized_;
}

value_type NLPDakota::max_var_bounds_viol() const
{
	return 1.0; // I don't know what to assume?
}

void NLPDakota::set_multi_calc(bool multi_calc) const
{
	multi_calc_ = multi_calc;
}

bool NLPDakota::multi_calc() const
{
	return multi_calc_;
}

// Overridden from NLPVarReductPerm

bool NLPDakota::nlp_selects_basis() const
{
	return false; // We don't know a basis
}

// Overridden protected methods from NLPSerialPreprocess

bool NLPDakota::imp_nlp_has_changed() const
{
	return false;  // There is no way to modify the nlp accept though the constructor
}

size_type NLPDakota::imp_n_orig() const
{
	return n_orig_;
}

size_type NLPDakota::imp_m_orig() const
{
	return m_orig_;
}

size_type NLPDakota::imp_mI_orig() const
{
	return mI_orig_;
}

const DVectorSlice NLPDakota::imp_xinit_orig() const
{
	return xinit_orig_;
}

bool NLPDakota::imp_has_var_bounds() const
{
	return has_var_bounds_;
}

const DVectorSlice NLPDakota::imp_xl_orig() const
{
	return xl_orig_;
}

const DVectorSlice NLPDakota::imp_xu_orig() const
{
	return xu_orig_;
}

const DVectorSlice NLPDakota::imp_hl_orig() const
{
	return hl_orig_;
}

const DVectorSlice NLPDakota::imp_hu_orig() const
{
	return hu_orig_;
}

void NLPDakota::imp_calc_f_orig(
	const DVectorSlice            &x_full
	,bool                        newx
	,const ZeroOrderInfoSerial   &zero_order_info
	) const
{
	imp_calc_point(CALC_F,x_full,newx,&zero_order_info,NULL,NULL);
}

void NLPDakota::imp_calc_c_orig(
	const DVectorSlice            &x_full
	,bool                        newx
	,const ZeroOrderInfoSerial   &zero_order_info
	) const
{
	imp_calc_point(CALC_C,x_full,newx,&zero_order_info,NULL,NULL);
}

void NLPDakota::imp_calc_h_orig(
	const DVectorSlice            &x_full
	,bool                        newx
	,const ZeroOrderInfoSerial   &zero_order_info
	) const
{
	imp_calc_point(CALC_H,x_full,newx,&zero_order_info,NULL,NULL);
}

void NLPDakota::imp_calc_Gf_orig(
	const DVectorSlice            &x_full
	,bool                        newx
	,const ObjGradInfoSerial     &obj_grad_info
	) const
{
	imp_calc_point(CALC_GF,x_full,newx,NULL,&obj_grad_info,NULL);
}

void NLPDakota::imp_report_orig_final_solution(
	const DVectorSlice      &x_orig
	,const DVectorSlice     *lambda_orig
	,const DVectorSlice     *lambdaI_orig
	,const DVectorSlice     *nu_orig
	,bool                  is_optimal
	) const
{
	// I don't know where to send this stuff?  I could save it in a
	// file or something for later use.
}

// Overridden protected methods from NLPSerialPreprocessExplJac

size_type NLPDakota::imp_Gc_nz_orig() const
{
	return Gc_orig_nz_;
}

size_type NLPDakota::imp_Gh_nz_orig() const
{
	return Gh_orig_nz_;
}

void NLPDakota::imp_calc_Gc_orig(
	const DVectorSlice& x_full, bool newx
	, const FirstOrderExplInfo& first_order_expl_info
	) const
{
	imp_calc_point(CALC_GC,x_full,newx,NULL,NULL,&first_order_expl_info);
}

void NLPDakota::imp_calc_Gh_orig(
	const DVectorSlice& x_full, bool newx
	, const FirstOrderExplInfo& first_order_expl_info
	) const
{
	imp_calc_point(CALC_GH,x_full,newx,NULL,NULL,&first_order_expl_info);
}

// Private member functions

void NLPDakota::assert_is_initialized() const
{
	THROW_EXCEPTION(
		!is_initialized_, std::logic_error
		,"NLPDakota::assert_is_initialized(): Error, the object has not been initialized properly!"
		);
}

void NLPDakota::imp_calc_point(
	ERequiredFunc                required
	,const DVectorSlice           &x_full
	,bool                        newx
	,const ZeroOrderInfoSerial   *zero_order_info
	,const ObjGradInfoSerial     *obj_grad_info
	,const FirstOrderExplInfo    *first_order_expl_info
	) const
{
	using LinAlgOpPack::Vp_StV;
	typedef FirstOrderExplInfo::val_t     G_val_t;
	typedef FirstOrderExplInfo::ivect_t   G_ivect_t;
	typedef FirstOrderExplInfo::jvect_t   G_jvect_t;

	Cout << "\nEntering NLPDakota::imp_calc_point(...) ...\n";

//	throw std::logic_error("NLPDakota::imp_calc_point(): End, dam it!");

	if( newx ) {
		f_orig_updated_ = false;
		c_orig_updated_ = false;
		h_orig_updated_ = false;
		Gf_orig_updated_ = false;
		Gc_orig_updated_ = false;
		Gh_orig_updated_ = false;
		my_vec_view(dakota_functions_) = 0.0;
	}

	const DVectorSlice x_orig = x_full(1,n_orig_);

	//
	// Determine which quantities to compute
	//

	const bool calc_f  = ( required==CALC_F  || multi_calc_ )                                      && !f_orig_updated_;
	const bool calc_c  = ( required==CALC_C  || multi_calc_ )                                      && !c_orig_updated_;
	const bool calc_h  = ( required==CALC_H  || multi_calc_ )                                      && !h_orig_updated_;
	const bool calc_Gf = ( required==CALC_GF || ( (int)required >= (int)CALC_GF && multi_calc_ ) ) && !Gf_orig_updated_;
	const bool calc_Gc = ( required==CALC_GC || ( (int)required >= (int)CALC_GF && multi_calc_ ) ) && !Gc_orig_updated_;
	const bool calc_Gh = ( required==CALC_GH || ( (int)required >= (int)CALC_GF && multi_calc_ ) ) && !Gh_orig_updated_;

	if( !calc_f && !calc_c && !calc_h && !calc_Gf && !calc_Gc && !calc_Gh )
		return; // Everything is already computed at this point!

	value_type      *f         = NULL;
	DVector         *c         = NULL;
	DVector         *h         = NULL;
	DVector         *Gf        = NULL;
	G_val_t         *Gc_val    = NULL;
	G_ivect_t       *Gc_ivect  = NULL;
	G_jvect_t       *Gc_jvect  = NULL;
	G_val_t         *Gh_val    = NULL;
	G_ivect_t       *Gh_ivect  = NULL;
	G_jvect_t       *Gh_jvect  = NULL;

	if(zero_order_info) {
		if(calc_f) f = zero_order_info->f;
		if(calc_c) c = zero_order_info->c;
		if(calc_h) h = zero_order_info->h;
	}
	else if(obj_grad_info) {
		if(calc_f)  f  = obj_grad_info->f;
		if(calc_c)  c  = obj_grad_info->c;
		if(calc_h)  h  = obj_grad_info->h;
		if(calc_Gf) Gf = obj_grad_info->Gf;
	}
	else if(first_order_expl_info) {
		if(calc_f)  f  = first_order_expl_info->f;
		if(calc_c)  c  = first_order_expl_info->c;
		if(calc_h)  h  = first_order_expl_info->h;
		if(calc_Gf) Gf = first_order_expl_info->Gf;
		if(calc_Gc) {
			Gc_val   = first_order_expl_info->Gc_val;
			Gc_ivect = first_order_expl_info->Gc_ivect;
			Gc_jvect = first_order_expl_info->Gc_jvect;
		}
		if(calc_Gh) {
			Gh_val   = first_order_expl_info->Gh_val;
			Gh_ivect = first_order_expl_info->Gh_ivect;
			Gh_jvect = first_order_expl_info->Gh_jvect;
		}
	}
	else {
		THROW_EXCEPTION(
			!(zero_order_info || obj_grad_info || first_order_expl_info), std::logic_error
			,"NLPDakota::imp_calc_point(): Error!"
			);
	}

	//
	// Set the Dakota active-set vector.
	//
	// Dakota sorts the responce functions as general inequalities first
	// and general equalities second.
	//

	std::fill_n( &dakota_asv_[0], dakota_asv_.length(), 0 );

	const int num_obj_funcs = multi_obj_weights_.length();

	if( calc_f || calc_Gf ) {
		std::fill_n(
			&dakota_asv_[0], num_obj_funcs
			,(calc_f  ? 1 : 0) + (calc_Gf ? 2 : 0)
			);
	}
	if( ( calc_c || calc_Gc ) && num_nonlin_eq_ ) {
		std::fill_n(
			&dakota_asv_[num_obj_funcs + num_nonlin_ineq_], num_nonlin_eq_
			,(calc_c  ? 1 : 0) + (calc_Gc ? 2 : 0)
			);
	}
	if( ( calc_h || calc_Gh ) && num_nonlin_ineq_ ) {
		std::fill_n(
			&dakota_asv_[num_obj_funcs], num_nonlin_ineq_
			,(calc_h  ? 1 : 0) + (calc_Gh ? 2 : 0)
			);
	}

	//
	// Compute the nonlinear functions
	//

	my_vec_view(dakota_x_) = x_orig;
	model_->continuous_variables(dakota_x_);
	model_->compute_response(dakota_asv_);
	const Dakota::Response&   local_response           = model_->current_response();
	const Dakota::RealVector& local_function_values    = local_response.function_values();
	const Dakota::RealMatrix& local_function_gradients = local_response.function_gradients();

	// Look for failed response
	const bool eval_failed =  (local_function_values[0] == dakota_failed_value_);

	//
	// Map to the NLP objects
	//

	if(!eval_failed) {

		//
		// The evaluation succeeded so copy the values
		//

		// f, Gf
		if(calc_f)  *f  = 0.0;
		if(calc_Gf) *Gf = 0.0;
		for( int i = 0; i < num_obj_funcs; ++i ) {
			if(calc_f) {
				*f  += multi_obj_weights_[i] * local_function_values[i];
				dakota_functions_[i] = local_function_values[i]; // Save in dakota_functions
			}
			if(calc_Gf) {
				Vp_StV(
					&(*Gf)(1,n_orig_), multi_obj_weights_[i]
					,DVectorSlice(&const_cast<Dakota::RealMatrix&>(local_function_gradients)[i][0],n_orig_)
					);
			}
		}
		if(calc_Gf && Gf->dim() > n_orig_ )
			(*Gf)(n_orig_+1,Gf->dim()) = 0.0; // Zero for slack variables
		
		// c = [ A_lin_eq' * x - lin_eq_targ                                                                  ]
		//     [ response(num_obj_funcs+num_nonlin_ineq+1:num_obj_funs+num_nonlin_ineq+num_nonlin_eq) - nonlin_eq_targ ]
		if( calc_c ) {
			if( num_lin_eq_ ) {
				DVectorSlice c_lin = (*c)(1,num_lin_eq_);
				V_mV( &c_lin, my_vec_view(dakota_rsqp_opt_->lin_eq_targ()) );
				const Dakota::RealMatrix  &lin_eq_jac = dakota_rsqp_opt_->lin_eq_jac();
				for( int j = 0; j < num_lin_eq_; ++j ) {
					for( int i = 0; i < n_orig_; ++i ) {
						c_lin[j] += lin_eq_jac[j][i] * x_orig[i];
					}
				}
			}
			if( num_nonlin_eq_ ) {
				const value_type
					*nonlin_eq_ptr = &const_cast<Dakota::RealVector&>(local_function_values)[num_obj_funcs+num_nonlin_ineq_];
				V_VmV(
					&(*c)(num_lin_eq_+1,num_lin_eq_+num_nonlin_eq_)
					,DVectorSlice(const_cast<value_type*>(nonlin_eq_ptr),num_nonlin_eq_)
					,my_vec_view(dakota_rsqp_opt_->nonlin_eq_targ())
					);
				// Save in dakota_functions
				std::copy( nonlin_eq_ptr, nonlin_eq_ptr + num_nonlin_eq_, &dakota_functions_[num_obj_funcs+num_nonlin_ineq_] );
			}
		}
		
		// h = [ A_lin_ineq' * x                                   ]
		//     [ response(num_obj_funcs+1:num_obj_funs+num_lin_eq) ]
		if( calc_h ) {
			//
			if( num_lin_ineq_ ) {
				DVectorSlice h_lin = (*h)(1,num_lin_ineq_);
				const Dakota::RealMatrix  &lin_ineq_jac = dakota_rsqp_opt_->lin_ineq_jac();
				for( int j = 0; j < num_lin_ineq_; ++j ) {
					for( int i = 0; i < n_orig_; ++i ) {
						h_lin[j] += lin_ineq_jac[j][i] * x_orig[i];
					}
				}
			}
			//
			if( num_nonlin_ineq_ ) {
				const value_type
					*nonlin_ineq_ptr = &const_cast<Dakota::RealVector&>(local_function_values)[num_obj_funcs];
				(*h)(num_lin_ineq_+1,num_lin_ineq_+num_nonlin_ineq_)
					= DVectorSlice(const_cast<value_type*>(nonlin_ineq_ptr),num_nonlin_ineq_);
				// Save in dakota_functions
				std::copy( nonlin_ineq_ptr, nonlin_ineq_ptr + num_nonlin_ineq_, &dakota_functions_[num_obj_funcs] );
			}
		}
		
		// Gc = [ A_lin_eq'                                     ]
		//      [ response.grad[num_nonlin_ineq+1]'             ]
		//      [ ..                                            ]
		//      [ response.grad[num_nonlin_ineq+num_nonlin_eq]' ]
		if( calc_Gc ) {
			index_type nz = 0;
			if( num_lin_eq_ ) {
				const Dakota::RealMatrix  &lin_eq_jac = dakota_rsqp_opt_->lin_eq_jac();
				for( int j = 1; j <= num_lin_eq_; ++j ) {
					for( int i = 1; i <= n_orig_; ++i ) {
						(*Gc_val)[nz]   = lin_eq_jac[j-1][i-1];
						(*Gc_ivect)[nz] = i;
						(*Gc_jvect)[nz] = j;
						++nz;
					}
				}
			}
			if( num_nonlin_eq_ ) {
				for( int j = 1; j <= num_nonlin_eq_; ++j ) {
					for( int i = 1; i <= n_orig_; ++i ) {
						(*Gc_val)[nz]   = local_function_gradients[num_obj_funcs+num_nonlin_ineq_+j-1][i-1];
						(*Gc_ivect)[nz] = i;
						(*Gc_jvect)[nz] = j + num_lin_eq_;
						++nz;
					}
				}
			}
		}
		
		// Gh = [ A_lin_ineq'                   ]
		//      [ response.grad[1]'             ]
		//      [ ..                            ]
		//      [ response.grad[num_nonlin_eq]' ]
		if( calc_Gh ) {
			index_type nz = 0;
			if( num_lin_ineq_ ) {
				const Dakota::RealMatrix  &lin_ineq_jac = dakota_rsqp_opt_->lin_ineq_jac();
				for( int j = 1; j <= num_lin_ineq_; ++j ) {
					for( int i = 1; i <= n_orig_; ++i ) {
						(*Gh_val)[nz]   = lin_ineq_jac[j-1][i-1];
						(*Gh_ivect)[nz] = i;
						(*Gh_jvect)[nz] = j;
						++nz;
					}
				}
			}
			if( num_nonlin_ineq_ ) {
				for( int j = 1; j <= num_nonlin_ineq_; ++j ) {
					for( int i = 1; i <= n_orig_; ++i ) {
						(*Gh_val)[nz]   = local_function_gradients[num_obj_funcs+j-1][i-1];
						(*Gh_ivect)[nz] = i;
						(*Gh_jvect)[nz] = j;
						++nz;
					}
				}
			}
		}
	}
	else {
		//
		// The evaluation failed so just fill everything with
		// and invalid number that will trigger a back tracking
		//

		// f, Gf
		if(calc_f)  *f  = invalid_func_value_;
		if(calc_Gf) *Gf = invalid_func_value_;
		// c
		if(calc_c)  *c = invalid_func_value_;
		// h
		if(calc_h) *h = invalid_func_value_;
		// Gc
		if(calc_Gc) {
			index_type nz = 0;
			for( int j = 1; j <= m_orig_; ++j ) {
				for( int i = 1; i <= n_orig_; ++i ) {
					(*Gc_val)[nz]   = invalid_func_value_;
					(*Gc_ivect)[nz] = i;
					(*Gc_jvect)[nz] = j;
					++nz;
				}
			}
		}
		// Gh
		if( calc_Gh ) {
			index_type nz = 0;
			for( int j = 1; j <= mI_orig_; ++j ) {
				for( int i = 1; i <= n_orig_; ++i ) {
					(*Gh_val)[nz]   = invalid_func_value_;
					(*Gh_ivect)[nz] = i;
					(*Gh_jvect)[nz] = j;
					++nz;
				}
			}
		}

	}

	if(calc_f)   f_orig_updated_  = true;
	if(calc_c)   c_orig_updated_  = true;
	if(calc_h)   h_orig_updated_  = true;
	if(calc_Gf)  Gf_orig_updated_ = true;
	if(calc_Gc)  Gc_orig_updated_ = true;
	if(calc_Gh)  Gh_orig_updated_ = true;

}

} // end namespace NLPInterfacePack

namespace Dakota {

//
// rSQPOptimizer
//

// This constructor accepts a Model
rSQPOptimizer::rSQPOptimizer(Model& model)
	:Optimizer(model)
	,model_(&model)
	 ,nlp_(&model,this
		,numLinearEqConstraints,numNonlinearEqConstraints
		,numLinearIneqConstraints,numNonlinearIneqConstraints)
{}

rSQPOptimizer::~rSQPOptimizer()
{}

void rSQPOptimizer::find_optimum()
{

	//------------------------------------------------------------------
	//     Solve the problem.
	//------------------------------------------------------------------
	
	namespace mmp   = MemMngPack;
	namespace mp    = MoochoPack;
	using mp::MoochoSolver;

	Cout << "\nCalling rSQP++ to solve the NLP! ...\n";

	if(model_->dv()) {
		Cout << "\nWarning!!! The dakota model has " << model_->dv() << " discrete variables "
			 << "associated with it that will be ignored by this optimizer!";
	}
	
	try {
		
		MoochoSolver  solver;

/*
		nlp_.initialize(true);

		// Terminate early!
		Cout << "\nrSQPOptimizer::find_optimum(): Terminate early!\n";
		bestVariablesArray.front().continuous_variables(model_->cv());
		RealVector dummy_func_values(model_->num_functions());
		bestResponseArray.front().function_values(dummy_func_values);
		return; // Don't do anything!
*/

		solver.set_nlp( mmp::rcp(&nlp_,false) );

		std::ofstream Moocho_out("MoochoConsole.out");
		solver.set_console_out(mmp::rcp(&Moocho_out,false));
		solver.set_error_handling(true,mmp::rcp(&Moocho_out,false));

		const MoochoSolver::ESolutionStatus
			solution_status = solver.solve_nlp();

		
	}
	catch(const std::exception& excpt) {
		std::cerr << "\nrSQPOptimizer::find_optimum(): Caught a std::exception: " << excpt.what() << std::endl;
	}
	catch(...) {
		std::cerr << "\nrSQPOptimizer::find_optimum(): Caught an unknown exception: \n";
	}

	//------------------------------------------------------------------
	//     Save the final results.
	//------------------------------------------------------------------
	
	bestVariablesArray.front().continuous_variables(nlp_.dakota_x());     // Just use the last point computed
	bestResponseArray.front().function_values(nlp_.dakota_functions());  // ""

}

} // namespace Dakota
